#include "CSBakeLightVolumes.fxh"

cbuffer cbPerFrame
{
	float4x4 gViewMatrix;
};

#ifndef NO_SHADOWS
#define NUM_CASCADES 4
#endif

cbuffer cbPerLight
{
	float3 gLightDirW;
	float4 gLightColorAndIntens;
#ifndef NO_SHADOWS
	float4x4 gLightViewProjs[NUM_CASCADES];
	float4 gCascadeDepths;
#endif
};

#ifndef NO_SHADOWS
SamplerComparisonState ShadowSampler
{
   // sampler state
   Filter = MIN_MAG_LINEAR_MIP_POINT;
   AddressU = MIRROR;
   AddressV = MIRROR;

   // sampler comparison state
   ComparisonFunc = LESS;
   Filter = COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
};

Texture2D gBigShadowMap;

float calculateShadowFactor(float2 texCoordBase, float4 lightSpacePos)
{
	lightSpacePos = lightSpacePos / lightSpacePos.w;

	const float depth = lightSpacePos.z;
	const float2 smTexCoord = texCoordBase + (lightSpacePos.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f)) * 0.5f;

	return gBigShadowMap.SampleCmpLevelZero(ShadowSampler, smTexCoord, depth);
}
#endif

#ifdef AXES_4
void storeLighting(int3 texCoord, float3 lightStrengthDir, float3 lightColor)
{
	const float3 axis0 = float3(0, 1, 0);
	const float3 axis1 = float3(0, -0.341987, 0.939705);
	const float3 axis2 = float3( 0.813808, -0.341987, -0.469852);
	const float3 axis3 = float3(-0.813808, -0.341987, -0.469852);

	float3 axis0Val = lightColor * max(lightStrengthDir.y, 0); // unnecessary to do dot, since axis0 is standard axis
	float3 axis1Val = lightColor * max(dot(lightStrengthDir, axis1), 0);
	float3 axis2Val = lightColor * max(dot(lightStrengthDir, axis2), 0);
	float3 axis3Val = lightColor * max(dot(lightStrengthDir, axis3), 0);

	write4Axes(texCoord, axis0Val, axis1Val, axis2Val, axis3Val);
}
#else
void storeLighting(int3 texCoord, float3 lightStrengthDir, float3 lightColor)
{
	float3 xPos = lightColor * max( lightStrengthDir.x, 0);
	float3 xNeg = lightColor * max(-lightStrengthDir.x, 0);
	float3 yPos = lightColor * max( lightStrengthDir.y, 0);
	float3 yNeg = lightColor * max(-lightStrengthDir.y, 0);
	float3 zPos = lightColor * max( lightStrengthDir.z, 0);
	float3 zNeg = lightColor * max(-lightStrengthDir.z, 0);

	write6Axes(texCoord, xPos, xNeg, yPos, yNeg, zPos, zNeg);
}
#endif

[numthreads(8, 8, 8)]
void CS(	uint3 GroupID : SV_GroupID,
			uint3 DispatchThreadID : SV_DispatchThreadID,
			uint3 GroupThreadID : SV_GroupThreadID,
			uint GroupIndex : SV_GroupIndex )
{
	const float3 lightColor = gLightColorAndIntens.rgb;
	const float lightIntensity = gLightColorAndIntens.a;

	float3 lightVolPos = gLightVolPosTex.Load(int2(GroupID.x, 0)).xyz;
	float3 lightVolScale = gLightVolScaleTex.Load(int2(GroupID.x, 0)).xyz;

	int threadOffsetX = gLightVolSectionThreadOffsetXTex.Load(int2(GroupID.x, 0)).x;
	float3 threadPosInSection = float3(DispatchThreadID - int3(threadOffsetX, 0, 0)) + float3(0.5f, 0.5f, 0.5f);

	float3 samplePosW = lightVolPos + threadPosInSection * lightVolScale;
	float3 samplePosV = mul(float4(samplePosW, 1.0f), gViewMatrix).xyz;

	float shadowFactor = 1.0f;

#ifndef NO_SHADOWS
	if(samplePosV.z > gCascadeDepths.z)
	{
		float4 lightSpacePos = mul(float4(samplePosW, 1.0f), gLightViewProjs[3]);
		shadowFactor = calculateShadowFactor(float2(0.5, 0.5), lightSpacePos);
	}
	else if(samplePosV.z > gCascadeDepths.y)
	{
		float4 lightSpacePos = mul(float4(samplePosW, 1.0f), gLightViewProjs[2]);
		shadowFactor = calculateShadowFactor(float2(0.0, 0.5), lightSpacePos);
	}
	else if(samplePosV.z > gCascadeDepths.x)
	{
		float4 lightSpacePos = mul(float4(samplePosW, 1.0f), gLightViewProjs[1]);
		shadowFactor = calculateShadowFactor(float2(0.5, 0.0), lightSpacePos);
	}
	else
	{
		float4 lightSpacePos = mul(float4(samplePosW, 1.0f), gLightViewProjs[0]);
		shadowFactor = calculateShadowFactor(float2(0.0, 0.0), lightSpacePos);
	}
#endif

	float3 lightStrengthDir = (-gLightDirW) * lightIntensity * shadowFactor;

	storeLighting(DispatchThreadID, lightStrengthDir, lightColor);


	//gLightXPos[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( lightStrengthDir.x, 0));
	//gLightXNeg[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max(-lightStrengthDir.x, 0));
	//gLightYPos[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( lightStrengthDir.y, 0));
	//gLightYNeg[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max(-lightStrengthDir.y, 0));
	//gLightZPos[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( lightStrengthDir.z, 0));
	//gLightZNeg[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max(-lightStrengthDir.z, 0));

	//gLightXPos[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( dot(lightStrengthDir, gAxis0), 0));
	//gLightXNeg[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( dot(lightStrengthDir, gAxis1), 0));
	//gLightYPos[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( dot(lightStrengthDir, gAxis2), 0));
	//gLightYNeg[DispatchThreadID] = float4_to_rgba8_as_uint(lightColor * max( dot(lightStrengthDir, gAxis3), 0));

}

technique11 tech
{
	pass P0
	{
		SetComputeShader(CompileShader(cs_5_0, CS())); 
	}
}
